package main

import (
	"context"
	"fmt"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	logger := newLogger()
	defer func() {
		_ = logger.Sync()
	}()

	logger.Info("starting zap demo",
		zap.String("component", "zap-demo"),
		zap.Time("timestamp", time.Now()),
	)

	ctx := context.WithValue(context.Background(), requestIDKey{}, "req-12345")
	sugar := logger.Sugar()

	doWork(ctx, logger, sugar)

	logger.Info("finished zap demo")
}

type requestIDKey struct{}

func newLogger() *zap.Logger {
	cfg := zap.NewProductionConfig()
	cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
	cfg.DisableStacktrace = false
	cfg.Sampling = nil
	cfg.EncoderConfig.TimeKey = "timestamp"
	cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	cfg.InitialFields = map[string]interface{}{
		"service": "zap-demo",
		"env":     "development",
	}

	logger, err := cfg.Build()
	if err != nil {
		panic(fmt.Errorf("build zap logger: %w", err))
	}

	return logger
}

func doWork(ctx context.Context, logger *zap.Logger, sugar *zap.SugaredLogger) {
	requestID, _ := ctx.Value(requestIDKey{}).(string)

	logger.Info("processing request",
		zap.String("request_id", requestID),
		zap.Int("step", 1),
	)

	sugar.Infof("pretending to fetch user for %s", requestID)

	if err := simulateError(); err != nil {
		logger.Error("failed processing request",
			zap.String("request_id", requestID),
			zap.Error(err),
		)
	}
}

func simulateError() error {
	time.Sleep(150 * time.Millisecond)
	return fmt.Errorf("upstream timed out")
}
